package models;

import goldengine.java.GOLDParser;
import goldengine.java.ParserException;
import goldengine.java.Reduction;
import goldengine.java.Token;

import java.io.File;

import parser.generic.Node;
import constants.ModelRuleConstants;


public class ModelParser {

	private Model model;
	
	public Model parse(String file, String compiledGrammar, boolean verbose) {

		String absoluteFilePath = new File(file).getAbsolutePath();
		String absoluteCGPath = new File(compiledGrammar).getAbsolutePath();

		GOLDParser parser = new GOLDParser();

		try {
			parser.loadCompiledGrammar(absoluteCGPath);
			parser.openFile(absoluteFilePath);
			
		} catch (ParserException parse) {
			errors.Error.report("**PARSER ERROR**\n" + parse.toString());
		}

		boolean done = false;
		int response = -1;
		
		while (!done) {
			
			try {
				response = parser.parse();
			} catch (ParserException parse) {
				if (verbose)
				System.out.println("**PARSER ERROR**\n" + parse.toString());
				System.exit(1);
			}

			switch (response) {
			case goldengine.java.GPMessageConstants.gpMsgTokenRead:
				/*
				 * A token was read by the parser. The Token Object can be
				 * accessed through the CurrentToken() property:
				 * Parser.CurrentToken
				 */
				break;

			case goldengine.java.GPMessageConstants.gpMsgReduction:
				/*
				 * This message is returned when a rule was reduced by the parse
				 * engine. The CurrentReduction property is assigned a Reduction
				 * object containing the rule and its related tokens. You can
				 * reassign this property to your own customized class. If this
				 * is not the case, this message can be ignored and the
				 * Reduction object will be used to store the parse tree.
				 */
				
				// Parser.Reduction = //Object you created to store the rule
				parser.setCurrentReduction(getNode(parser.currentReduction()));
				
				// ************************************** log file
				// System.out.println("gpMsgReduction");
				// Reduction myRed = parser.currentReduction();
				// ************************************** end log

				break;

			case goldengine.java.GPMessageConstants.gpMsgAccept:
				/* The program was accepted by the parsing engine */

				// ************************************** log file
				if (verbose)
					System.out.println("gpMsgAccept");
				// ************************************** end log

				done = true;

				break;

			case goldengine.java.GPMessageConstants.gpMsgLexicalError:
				/*
				 * Place code here to handle a illegal or unrecognized token To
				 * recover, pop the token from the stack: Parser.PopInputToken
				 */

				// ************************************** log file
				if (verbose)
					System.out.println("gpMsgLexicalError");
				// ************************************** end log

				parser.popInputToken();

				break;

			case goldengine.java.GPMessageConstants.gpMsgNotLoadedError:
				/* Load the Compiled Grammar Table file first. */

				// ************************************** log file
				if (verbose)
					System.out.println("gpMsgNotLoadedError");
				// ************************************** end log

				done = true;

				break;

			case goldengine.java.GPMessageConstants.gpMsgSyntaxError:
				/*
				 * This is a syntax error: the source has produced a token that
				 * was not expected by the LALR State Machine. The expected
				 * tokens are stored into the Tokens() list. To recover, push
				 * one of the expected tokens onto the parser's input queue (the
				 * first in this case): You should limit the number of times
				 * this type of recovery can take place.
				 */

				done = true;

				Token theTok = parser.currentToken();
				System.out.println("Token not expected: "
						+ (String) theTok.getData());

				// ************************************** log file
				if (verbose)
					System.out.println("gpMsgSyntaxError");
				// ************************************** end log

				break;

			case goldengine.java.GPMessageConstants.gpMsgCommentError:
				/*
				 * The end of the input was reached while reading a comment.
				 * This is caused by a comment that was not terminated
				 */

				// ************************************** log file
				if (verbose)
					System.out.println("gpMsgCommentError");
				// ************************************** end log

				done = true;

				break;

			case goldengine.java.GPMessageConstants.gpMsgInternalError:
				/*
				 * Something horrid happened inside the parser. You cannot
				 * recover
				 */

				// ************************************** log file
				if (verbose)
					System.out.println("gpMsgInternalError");
				// ************************************** end log

				done = true;

				break;
			}
		}
		try {
			parser.closeFile();
		} catch (ParserException parse) {
			if (verbose)
				System.out.println("**PARSER ERROR**\n" + parse.toString());
			System.exit(1);
		}
		
		return model;
	}

	public Node getNode(Reduction reduction)
	{
		switch(reduction.getParentRule().getTableIndex())
        {
           case ModelRuleConstants.RULE_MODEL_MODEL_IDENTIFIER_COLON_IDENTIFIER_EQ_LBRACE_RBRACE:
              //<Model> ::= model Identifier ':' Identifier '=' '{' <Objects> '}'
              model = new Model((String)reduction.getToken(1).getData(), (String)reduction.getToken(3).getData(), (Instances)reduction.getToken(6).getData());
              return model;
           case ModelRuleConstants.RULE_OBJECTS:
              //<Objects> ::= <Object>
        	  Instances objs = new Instances();
              objs.addInstance((Instance)reduction.getToken(0).getData());
              return objs;
           case ModelRuleConstants.RULE_OBJECTS2:
              //<Objects> ::= <Object> <Objects>
        	   
        	   if (reduction.getToken(1).getData() instanceof Instance)
        	   {
        		   objs = new Instances();
        		   objs.addInstance((Instance)reduction.getToken(0).getData());
        		   objs.addInstance((Instance)reduction.getToken(1).getData());
        		   return objs;
        	   }
        	   
        	   objs = (Instances)reduction.getToken(1).getData();
        	   objs.addInstance((Instance)reduction.getToken(0).getData());
        	   return objs;
        	   
           case ModelRuleConstants.RULE_OBJECT_OBJECT_IDENTIFIER_COLON_IDENTIFIER_EQ_LBRACE_RBRACE:
              //<Object> ::= object Identifier ':' Identifier '=' '{' <Fields> '}'
        	   Fields fields;
        	   if (reduction.getToken(6).getData() instanceof Field)
        	   {
        		   fields = new Fields();
        		   fields.addField((Field)reduction.getToken(6).getData());
        	   }
        	   else
        	   {
        		   fields = (Fields)reduction.getToken(6).getData();
        	   }
        	   
              return new Instance((String)reduction.getToken(1).getData(), (String)reduction.getToken(3).getData(), fields);
           case ModelRuleConstants.RULE_FIELDS:
              //<Fields> ::= <Field>
              fields = new Fields();
              fields.addField((Field)reduction.getToken(0).getData());
              return fields;
           case ModelRuleConstants.RULE_FIELDS2:
              //<Fields> ::= <Field> <Fields>
        	   
        	   if (reduction.getToken(1).getData() instanceof Field)
        	   {
        		   fields = new Fields();
        		   fields.addField((Field)reduction.getToken(0).getData());
        		   fields.addField((Field)reduction.getToken(1).getData());
        		   return fields;
        	   }
        	   
        	   fields = (Fields)reduction.getToken(1).getData();
        	   fields.addField((Field)reduction.getToken(0).getData());
        	   return fields;
           case ModelRuleConstants.RULE_FIELD_IDENTIFIER_COLON_IDENTIFIER_EQ_SEMI:
              //<Field> ::= Identifier ':' Identifier '=' <Value> ';'
              return new Field((String)reduction.getToken(0).getData(), (String)reduction.getToken(2).getData(), (Value)reduction.getToken(4).getData());
           case ModelRuleConstants.RULE_VALUE_STRINGLITERAL:
              //<Value> ::= StringLiteral
              return new StringValue((String)reduction.getToken(0).getData());
           case ModelRuleConstants.RULE_VALUE:
              //<Value> ::= <Set>
              return (SetValue)reduction.getToken(0).getData();
           case ModelRuleConstants.RULE_VALUE2:
              //<Value> ::= <Bool>
              return (BoolValue)reduction.getToken(0).getData();
           case ModelRuleConstants.RULE_VALUE3:
              //<Value> ::= <Item>
              return (Item)reduction.getToken(0).getData();
           case ModelRuleConstants.RULE_VALUE4:
              //<Value> ::= <Number>
              return (Number)reduction.getToken(0).getData();
           case ModelRuleConstants.RULE_NUMBER_NATURAL:
              //<Number> ::= Natural
              return new Number((String)reduction.getToken(0).getData());
           case ModelRuleConstants.RULE_NUMBER_POSITIVEREAL:
              //<Number> ::= PositiveReal
        	   return new Number((String)reduction.getToken(0).getData());
           case ModelRuleConstants.RULE_NUMBER_MINUS:
              //<Number> ::= '-' <Number>
        	   return new NegNumber((String)reduction.getToken(0).getData());
           case ModelRuleConstants.RULE_BOOL_TRUE:
              //<Bool> ::= true
              return new BoolValue(true);
           case ModelRuleConstants.RULE_BOOL_FALSE:
              //<Bool> ::= false
              return new BoolValue(false);
           case ModelRuleConstants.RULE_SET_LBRACE_RBRACE:
              //<Set> ::= '{' <Items> '}'
        	   
        	   Items items;
        	  if (reduction.getToken(1).getData() instanceof Item)
        	  {
        		  items = new Items();
        		  items.addItem((Item)reduction.getToken(1).getData());
        	  }
        	  else {
        		  items = (Items)reduction.getToken(1).getData();
        	  }
        	   
              return new SetValue(items);
           case ModelRuleConstants.RULE_SET_LBRACE_RBRACE2:
              //<Set> ::= '{' '}'
              return new SetValue();
           case ModelRuleConstants.RULE_ITEMS:
              //<Items> ::= <Item>
              items = new Items();
              items.addItem((Item)reduction.getToken(0).getData());
              return items;
           case ModelRuleConstants.RULE_ITEMS_COMMA:
              //<Items> ::= <Item> ',' <Items>       	  
        	   if (reduction.getToken(2).getData() instanceof Item)
        	   {
        		   items = new Items();
        		   items.addItem((Item)reduction.getToken(0).getData());
        		   items.addItem((Item)reduction.getToken(2).getData());
        		   return items;
        	   }
        	   
        	   items = (Items)reduction.getToken(2).getData();
        	   items.addItem((Item)reduction.getToken(0).getData());
        	   return items;
           case ModelRuleConstants.RULE_ITEM_IDENTIFIER:
              //<Item> ::= Identifier
              return new Item((String)reduction.getToken(0).getData());
        }

		return null;
	}
	
	public Model getModel()
	{
		return model;
	}
}
